package com.apobates.forum.toro.core;

import com.apobates.forum.toro.utils.BeanHelper;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Express表达式的解析器
 * 支持:
 * 1)通过表达式构造对象.使用parse方法
 * 2)通过表达式来实现自定义toString.使用format方法
 * 3)若只需要表达式的解析结果. toMap可以满足
 * @param <T> 属性表达式运行基础的对象类型
 */
public class BeanParser<T> {
    private final T instance;
    // 排好序了
    private final Map<String, Corner> formater;
    private final static String DEF_START = "{";
    private final static Logger logger = LoggerFactory.getLogger(BeanParser.class);

    public BeanParser(Class<T> beanClass, Map<String, Corner> formater) {
        this.instance = BeanHelper.buildInstance(beanClass);
        this.formater = sortByCornerRank(formater);
    }
    public BeanParser(T instance, Map<String, Corner> formater) {
        this.instance = instance;
        this.formater = sortByCornerRank(formater);
    }
    // 第七版: 五花马
    /* 将指定表达式分别分派给word的匹配器。并收集word的匹配器的匹配结果
    private Map<String,Object> kideBatch(String express, final String startSymbol) {
        logger.debug("[KB]express: "+ express+", start: "+startSymbol);
        Set<KideMatcher> mathers = this.buildKideMatcher(startSymbol);
        logger.debug("[K-MAT] current kide matcher print start");
        mathers.stream().forEach((ins)->logger.debug(ins.toString()));
        logger.debug("[K-MAT] current kide matcher print finish");
        // 增加一个前缀，以划定界限
        final String expression = (!express.startsWith(startSymbol) && null != startSymbol) ? startSymbol+express : express;
        // 所有的结束分隔符
        final Set<String> allTails = Collections.unmodifiableSet(this.getAllTailSymbol());
        Collection<KideMatcheResult> kmrset = mathers.stream().map(km->km.match(expression, allTails)).flatMap(tmp->tmp.stream()).collect(Collectors.toSet());
        Function<KideMatcheResult, Pair<String,Object>> convert = (kmr)->{
            return Pair.of(kmr.getAttribute(), kmr.getValue());
        };
        Map<String,Object> data = kmrset.stream().map(convert).collect(Collectors.toMap(pair->pair.getLeft(), pair->pair.getRight()));
        return this.supplyDefaultValue(data);
    }*/
    // 第八版: 空间地址
    // 基于分隔符的空间地址进行配对.实现word的逆匹配
    private Map<String,Object> lookSpaceAddr(String express, final String startSymbol) {
        // 增加一个前缀，以划定界限
        final String expression = (!express.startsWith(startSymbol) && null != startSymbol) ? startSymbol+express : express;
        final SpaceMatcher smIns = buildSpaceMatcher(startSymbol);
        Collection<SpaceMatcheResult> smrset = smIns.match(expression);
        Map<String,Object> data = smrset
                .stream()
                .map(smr->findSpaceMatchWord(smr, startSymbol))
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(pair->pair.getLeft(), pair->pair.getRight(), (oldV,newV)-> newV));
        // 打印调试日志
        if(logger.isDebugEnabled()) {
            data.entrySet().stream().forEach(entry -> logger.debug("[SML]match word: " + entry.getKey() + ", value: " + entry.getValue().toString()));
        }
        return data;
    }
    // 根据匹配器的结果查找关联的Word
    private List<Pair<String,Object>> findSpaceMatchWord(final SpaceMatcheResult smrIns, final String startSymbol){
        // 头匹配哪个word
        Set<String> headWord = new HashSet<>();
        // 尾匹配哪个word
        Set<String> tailWord = new HashSet<>();
        List<Pair<String,Object>> rs = new ArrayList<>();
        for(Map.Entry<String,Corner> entry : this.formater.entrySet()){
            Corner tmp = entry.getValue();
            String tmpHeadSymbol = tmp.getRanking()==0 ? startSymbol : this.getPrevious(tmp, this.formater).getDelimiter();
            String tmpTailSymbol = tmp.getDelimiter();
            // 尾分隔符可能匹配的word
            if (tmpTailSymbol.equals(smrIns.getTailSymbol())){
                tailWord.add(entry.getKey());
                // 若当前是可选的放到结果中
                if (!tmp.isRequired()) {
                    rs.add(Pair.of(entry.getKey(), tmp.getDefaultVal()));
                }
            }
            // 头分隔符可能匹配的word
            if (tmpHeadSymbol.equals(smrIns.getHeadSymbol())){
                headWord.add(entry.getKey());
            }
        }
        // 全匹配: 两个集合中是否有同一个word
        String fmWord = this.fullMatchWord(headWord, tailWord);
        if (null != fmWord) {
            rs.add(Pair.of(fmWord, smrIns.getValue()));
        } else { // 头匹配
            String headMatchWord = headWord.iterator().next();
            // 打印调试日志
            if (logger.isDebugEnabled()) {
                logger.debug("[SML][Word]头匹配：" + headMatchWord);
            }
            rs.add(Pair.of(headMatchWord, smrIns.getValue()));
        }
        return rs;
    }
    // 返回全匹配的Word,失败返回null
    private String fullMatchWord(Set<String> headWords, Set<String> tailWords){
        // 打印调试日志
        if(logger.isDebugEnabled()) {
            logger.debug("[SML][Word]参数：头>" + String.join(",", headWords));
            logger.debug("[SML][Word]参数：尾>" + String.join(",", tailWords));
        }
        HashSet<String> tmp = new HashSet<>(headWords);
        tmp.retainAll(tailWords);
        // 打印调试日志
        if(logger.isDebugEnabled()) {
            logger.debug("[SML][Word]全匹配：" + String.join(",", tmp));
            logger.debug("------->-------->");
        }
        return tmp.stream().findFirst().orElse(null);
    }
    // 构造空间匹配器
    private SpaceMatcher buildSpaceMatcher(final String startSymbol){
        // 头分隔符
        Set<String> heads = new HashSet<>();
        // 尾分隔符
        Set<String> tails = new HashSet<>();
        for(Map.Entry<String, Corner> entry : this.formater.entrySet()){
            // 可选可能存在多个尾分隔符
            Corner curCorner = entry.getValue();
            String _finish = curCorner.getDelimiter();
            String _start = curCorner.getRanking() == 0 ? startSymbol : this.getPrevious(curCorner, this.formater).getDelimiter();
            heads.add(_start);
            tails.add(_finish);
        }
        return new SpaceMatcher(heads, tails);
    }
    // 查看指定Word角注的上一个角注
    private Corner getPrevious(Corner current, final Map<String, Corner> sortStepMap) {
        Corner rs = null;
        for (Map.Entry<String, Corner> entry : sortStepMap.entrySet()) {
            if(current.getRanking() - entry.getValue().getRanking() == 1){
                rs = entry.getValue();
                break;
            }
        }
        return rs;
    }
    // 按Sequence排序
    private Map<String, Corner> sortByCornerRank(final Map<String, Corner> map) {
        // Static Method with return type Map and
        // extending comparator class which compares values
        // associated with two keys
        Comparator<String> valueComparator = new Comparator<>() {
            // return comparison results of values of
            // two keys
            public int compare(String k1, String k2) {
                int comp = map.get(k1).compareTo(map.get(k2));
                if (comp == 0) {
                    return 1;
                } else {
                    return comp;
                }
            }
        };
        // SortedMap created using the comparator
        Map<String, Corner> sorted = new TreeMap<>(valueComparator);
        sorted.putAll(map);
        if (logger.isDebugEnabled()) {
            for (Map.Entry<String, Corner> entry : sorted.entrySet()) {
                logger.debug("[KS]key: " + entry.getKey() + ", delimiter: " + entry.getValue().getDelimiter() + ", rank: " + entry.getValue().getRanking());
            }
        }
        return sorted;
    }
    /**
     * 使用属性值表达式构造对象实例
     * @param express 属性值表达式
     * @param startSymbol 开始符号
     * @return
     */
    public T parse(String express, String startSymbol) {
        T _instance = BeanHelper.buildInstance((Class<T>)this.instance.getClass());
        // 属性与值的map
        Map<String, Object> attrValMap = this.toMap(express, startSymbol);
        BeanHelper.setMultiAttrVal(_instance, attrValMap);
        return _instance;
    }
    /**
     * 使用属性值表达式构造对象实例
     * 注意:
     * 使用内置的开始字符: { ; 若表达式中已经存在请使用: parse(express, startSymbol)
     * @param express 属性值表达式
     * @return
     */
    public T parse(String express) {
        return this.parse(express, DEF_START);
    }
    /**
     * 将解析结果转换成Map
     * 注意:
     * 此时若使用Class参数实例化。请确保Class是可以实例化的.不建议使用实例的内部类
     * 此时若使用T实例实例化。不需要其属性有值
     * 方法是toMap的委托方法. 使用内置的开始字符: { ; 若表达式中已经存在请使用: toMap(express, startSymbol)
     * @param express 属性值表达式
     * @return key=属性名称, value=属性值
     */
    public Map<String,Object> toMap(String express){
        return this.toMap(express, DEF_START);
    }
    /**
     * 将解析结果转换成Map
     * 注意:
     * 此时若使用Class参数实例化。请确保Class是可以实例化的.不建议使用实例的内部类
     * 此时若使用T实例实例化。不需要其属性有值
     *
     * @param express 属性值表达式
     * @param startSymbol 开始符号
     * @return key=属性名称, value=属性值
     */
    public Map<String,Object> toMap(String express, String startSymbol){
        // 属性与值的map
        Map<String, Object> attrValMap = this.lookSpaceAddr(express, startSymbol);
        if(logger.isDebugEnabled()) {
            logger.debug("------------------------------->parse attr start");
            attrValMap.entrySet().stream().forEach(entry -> logger.debug(String.format("Attr:%s, Val:%s", entry.getKey(), entry.getValue().toString())));
            logger.debug("------------------------------->parse attr finish");
        }
        return attrValMap;
    }
    /**
     * 返回表达式定义的格式化结果(toString)
     * 注意: 需要实例化对像. 不能在使用Class的构造函数后调用此方法
     *
     * @return 若实例化时对象未初始化返回null
     */
    public String format(){
        if(null == this.instance){
            return null;
        }
        final Map<String, Corner> collect = this.formater;
        List<String> attrVal = new ArrayList<>();
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String,Corner> entry:collect.entrySet()){
            sb.append("%s");
            sb.append(entry.getValue().getDelimiter());
            attrVal.add(BeanHelper.getAttrVal(this.instance, entry.getKey()).toString());
        }
        return String.format(sb.toString(), attrVal.toArray());
    }
}
